/***************************************************************************
 *   mgpi_ehimdl.c - Events Handling Interface Model Definitions           *
 *   Copyright (C) 2008 by cmaster.matso                                   *
 *   cmaster.matso@gmail.com                                               *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/

#include "mgpi_ehimdl.h"

Uint32 g_nInterval = 25;

static MGPI_BOOL s_fAsyncFlag = MGPI_FALSE;                                     /** asynchronouse events handling ON/OFF flag */
static SDL_Thread *s_pEventsHandlingThread = NULL;                              /** SDL thread pointer for events handling */
static SDL_TimerID s_oTimer = 0;                                                /** SDL timer object used for time based events handling */

/**
 * Defines what is the action-performed routines array index according to the event type.
 */
static int s_event2Action(SDL_Event *evt) {
    switch (evt->type) {/* MODIFICATION: do it by using the enumerant... */
        case SDL_ACTIVEEVENT: return MGPI_ACTIVE_EVENT;
        case SDL_MOUSEBUTTONDOWN: return MGPI_MOUSE_BUTTON_DOWN;
        case SDL_MOUSEBUTTONUP: return MGPI_MOUSE_BUTTON_UP;
        case SDL_MOUSEMOTION: return MGPI_MOUSE_MOTION;
        case SDL_KEYDOWN: return MGPI_KEY_DOWN;
        case SDL_KEYUP: return MGPI_KEY_UP;
        case SDL_JOYAXISMOTION: return MGPI_JOY_AXIS_MOTION;
        case SDL_JOYBALLMOTION: return MGPI_JOY_BALL_MOTION;
        case SDL_JOYHATMOTION: return MGPI_JOY_HAT_MOTION;
        case SDL_JOYBUTTONDOWN: return MGPI_JOY_BUTTON_DOWN;
        case SDL_JOYBUTTONUP: return MGPI_JOY_BUTTON_UP;
        case SDL_VIDEORESIZE: return MGPI_VIDEO_RESIZE;
        case SDL_VIDEOEXPOSE: return MGPI_VIDEO_EXPOSE;
        case SDL_SYSWMEVENT: return MGPI_SYS_WM_EVENT;
    }
    return -1;
}

/**
 * Event handling asynchronous callback.
 */
static int s_asyncAction(void *ptr) {
    struct _asycevt *a = (struct _asycevt *) ptr;

    if (a->c) {
        if (a->evt->type != SDL_USEREVENT) {
            if (a->c->onEvent[a->index])
                a->c->onEvent[a->index](a->c, a->evt);
        } else {
            if (a->c->onUserEvent[a->index])
                a->c->onUserEvent[a->index](a->c, a->evt);
        }
    } else if (a->f) {
        a->f[a->index].onEvent(a->evt, a->f[a->index].ptr);
    }

    free(a);

    return (!0);
}

/**
 * Calls event handling routine asynchonously.
 */
static MGPI_BOOL s_callAsyncAction(int index, comp_t *c, filter_t *f, SDL_Event *evt) {
    struct _asycevt *a = (struct _asycevt *) calloc(1, sizeof (struct _asycevt));

    a->c = c;
    a->f = f;
    a->evt = evt;
    a->index = index;

    SDL_CreateThread(s_asyncAction, a);
    
    return MGPI_TRUE;
}

/**
 * Events handling loop thread routine.
 */
static int s_eventsHandlingLoop(void *param) {
    cont_t *ct = (cont_t *) param;
    float last_time = 0;
    float current_time = 0;

    while (g_fState) {
        current_time = (float) SDL_GetTicks();
        
        if ((current_time - last_time) >= 100.0f) {/* parameter... */
            
            /* TODO: call timer event routine... */

            last_time = current_time;
        }
        g_fState = MGPI_Listener(ct) && MGPI_UserListener(ct);
        SDL_Delay(10);
    }
    return 0;
}

/**
 * Filtered events handling loop thread routine.
 */
static int s_filteredEventsHandlingLoop(void *param) {
    filter_t *ft = (filter_t *) param;
    float last_time = 0;
    float current_time = 0;

    while (g_fState) {
        current_time = (float) SDL_GetTicks();

        if ((current_time - last_time) >= 100.0f) {/* parameter... */

            /* TODO: call timer event routine... */

            last_time = current_time;
        }

        g_fState = MGPI_FilteredListener(ft, ft->n);
        SDL_Delay(10);
    }
    return 0;
}

/**
 * Time driven events handling routine.
 */
static Uint32 s_timerEventsHandling(Uint32 interval, void *param) {
    cont_t *ct = (cont_t *) param;
    g_fState = MGPI_Listener(ct) && MGPI_UserListener(ct);
    return interval;
}

/**
 * Initializes the events handling thread.
 */
void MGPI_InitEvents(int mode, void *ptr) {
    cont_t *ct;
    filter_t *ft;
    
    switch (mode) {
        case MGPI_TIMER_EVENTS:
            ct = (cont_t *) ptr;
            if (!ct) s_oTimer = SDL_AddTimer(g_nInterval, s_timerEventsHandling, MGPI_GetRootContainer());
            else s_oTimer = SDL_AddTimer(g_nInterval, s_timerEventsHandling, ct);
            break;

        case MGPI_FILTERED_EVENTS:
            ft = (filter_t *) ptr;
            s_pEventsHandlingThread = SDL_CreateThread(s_filteredEventsHandlingLoop, ft);
            break;

        case MGPI_THREAD_EVENTS:
            ct = (cont_t *) ptr;
            if (!ct) s_pEventsHandlingThread = SDL_CreateThread(s_eventsHandlingLoop, MGPI_GetRootContainer());
            else  s_pEventsHandlingThread = SDL_CreateThread(s_eventsHandlingLoop, ct);
            break;

        default: break;
    }
    return;
}

/**
 * Turns on and off the asynchronous event handing.
 */
void MGPI_AsyncProc(void) {
    s_fAsyncFlag = !s_fAsyncFlag;
    return;
}

/**
 * Frees the events handling timer.
 */
void MGPI_FreeTimer(void) {
    if (s_oTimer != 0) {
        SDL_RemoveTimer(s_oTimer);
    }
    return;
}

/**
 * Listens to the SDL events over a container.
 */
MGPI_BOOL MGPI_Listener(cont_t *ct) {
    SDL_Event event;
    int i = 0;
/*
    int num;
    Uint8 *keys = SDL_GetKeyState(&num);

    if (keys[SDLK_ESCAPE]) return MGPI_FALSE;
*/
    if (SDL_QuitRequested()) return MGPI_FALSE;

    if (ct) {
        while (SDL_PollEvent(&event)) {
            i = s_event2Action(&event);
            if (i >= 0) {
                if (ct->component.onEvent[i]) {
                    if (s_fAsyncFlag)
                        return s_callAsyncAction(i, &ct->component, NULL, &event);
                    else
                        return ct->component.onEvent[i](&ct->component, &event);
                }
            }
        }
    } else {
        MGPI_PostMessagef("%s: given container is not properly set up", __FUN__);
        return MGPI_FALSE;
    }

    return MGPI_TRUE;
}

/**
 * Listens to the SDL user defined events over a container.
 */
MGPI_BOOL MGPI_UserListener(cont_t *ct) {
    SDL_Event event;

    if (ct->component.onUserEvent) {
        while (SDL_PollEvent(&event)) {
            if (event.type == SDL_USEREVENT) {
                if (s_fAsyncFlag)
                    return s_callAsyncAction(((usrevt_t *) event.user.data1)->index, &ct->component, NULL, &event);
                else if (ct->component.onUserEvent[((usrevt_t *) event.user.data1)->index])
                    return ct->component.onUserEvent
                        [((usrevt_t *) event.user.data1)->index](&ct->component, &event);
            }
        }
    }
    return MGPI_TRUE;
}

/**
 * Listens to all events and fires actions for those which are defined in the filters array.
 */
MGPI_BOOL MGPI_FilteredListener(filter_t *events, int n) {
    SDL_Event event;
    int i = 0;

    while (SDL_PollEvent(&event)) {
        while (i < n) {
            if (events[i].event_type == event.type) {
                if (s_fAsyncFlag)
                    return s_callAsyncAction(i, NULL, events, &event);
                else return events[i].onEvent(&event, events[i].ptr);
            }
            ++i;
        }
    }
    return MGPI_TRUE;
}
